# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: s; -*- 
#
# Copyright (C) 2008 - Grzes Furga
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.


import os, sys
import time

from OpenGL.GL import *
from OpenGL.GLU import *

import gtk
import gtk.gtkgl 

#import glFreeType # no text rendering for now
import math
import sys

SPEED = 0.5  #commit test



DARK_ORDER = [-5,-4,-3,-2,-1,5,4,3,2,1,0]
DARK_COLOR1 = [.0,.0,.0,1.0] 
DARK_COLOR2 = [0.5,0.5,0.5,1.0]

WHITE_ORDER = DARK_ORDER[:]
WHITE_ORDER.reverse()
WHITE_COLOR1 = [1.0,1.0,1.0,.0] 
WHITE_COLOR2 = [1.0,1.0,1.0,.3]



def deb(s):
    sys.stderr.write(s)
    

class TextureCache:
    def __init__(self, loader, missing, cache_size = 100):
        self._loader = loader
        self._cover_dir = "%s/.gnome2/rhythmbox/covers/" % os.getenv("HOME")
        self._nocover = 0 
        self._free = 1
        self.textures = [missing]

        self._nametable = {} # this should be a hash or sth
        
    def get(self, artist, album):
        path = "%s%s - %s.jpg" % (self._cover_dir, artist, album)
        try:
            return self._nametable[path]
        except KeyError:
            
            if not os.path.exists(path): 
                self._nametable[path] = self._nocover
                return self._nocover
                
            # not in nametable, but file exists: load 
            offset = self._free
            self.textures.append(glGenTextures(1))
            self._loader( path, self.textures[offset])
            self._nametable[path] = offset
            self._free += 1
            return offset
            
    def __del__(self): # we still need to call this explicitly in the proper gl context
        """free textures"""
        #for t in self.textures
        glDeleteTexture(self.textures)
        deb("removing textures\n")

class CoverFlowEngine():
    """Cover Flow Engine, the actual opengl rendering code, and animation math."""
    
    def __init__(self):
        """Initialize opengl rendering state."""
        # init cover flow state                
        self.songs = []
        self.songs_center = None
        
         
        # init open gl
        #self.font_renderer = glFreeType.font_data (os.path.join(os.path.dirname(__file__), 'monospace.ttf'), 20)
                    
        missing = texture_from_path(os.path.join(os.path.dirname(__file__), 'gnome.png'), glGenTextures(1))

        self.texture_cache = TextureCache(texture_from_path, missing)

        
        self.target = 0
        
        self.diff = 0. #opis
        """To jest opis diff"""
        
        self.diff_distance = 0.
        self.slow_at = 0.
        self.diff_dir = 1
        
        self.diff
        
        self.light = False      
        self.animate = False
        self.last_time = time.time()
        
        self.order = DARK_ORDER
        self.color1 = DARK_COLOR1
        self.color2 = DARK_COLOR2
        
    def toggle_light(self):
        self.light = not self.light
            
    def glstate_setup(self):
        # pure opengl calls
        #glDisable(GL_LIGHTING)
                
        glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST )
        glShadeModel( GL_SMOOTH )
        glEnable(GL_TEXTURE_2D)

        if self.light:
            glClearColor(1.0, 1.0, 1.0, 1.0)
            glEnable(GL_BLEND)
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
            glEnable(GL_DEPTH_TEST)
            self.order = WHITE_ORDER
            self.color1 = WHITE_COLOR1
            self.color2 = WHITE_COLOR2
                
        else:
            glClearColor(.0, .0, .0, 1.0)
            glDisable(GL_DEPTH_TEST)
            glDisable(GL_BLEND)
            self.order = DARK_ORDER
            self.color1 = DARK_COLOR1
            self.color2 = DARK_COLOR2

        glDepthFunc( GL_LEQUAL )        
        glClearDepth( 1.0 )
        
    def projection_setup(self, width, height):
        """Setup viewport and perspective - each time the window is resized."""
        glViewport(0, 0, width, height)

        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        
        gluPerspective(45.0, (width / float(height)), 0.1, 100.0) 
        glMatrixMode(GL_MODELVIEW)

                
    def load_covers(self, songs):
        self.target = 0
        self.diff = 0.
        self.songs = []
        for entryid, art, al, t in songs:
            self.songs.append((
                entryid, self.texture_cache.get(art,al), art, al, t))
        self.songs_len=len(self.songs)    
    

    def goto(self, entry_id):
        for (i,(eid, _, _, _, _)) in enumerate(self.songs):
            if entry_id==eid:
                self.diff_distance = self.target + self.diff*self.diff_distance*self.diff_dir - i
               
                self.target = i
                self.diff = 1.
                if self.diff_distance > 0.: 
                    self.diff_dir = 1
                     
                elif self.diff_distance < 0.: 
                    self.diff_dir = -1
                    self.diff_distance = -self.diff_distance
                else:
                    self.animate = False
                    break

                self.slow_at = max(0., 1.0 - (3.0 / self.diff_distance))
                self.animate = True
                self.last_time = time.time()
                break
        

    def draw(self):
        """Draw the current frame.
        Compute animation step if neccessary.
        """
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()        
        # if animate, check last timereference, animate diff + speed*timeslice
        # if diff>1 or <-1 and current != target next current
        # if done animate=false
        
        now = time.time()
        timeslice = now - self.last_time
        self.last_time = now
        
        self.diff -= SPEED * timeslice
            
        if self.diff <= .0:
            self.diff = .0
            self.animate = False                
        
        s = self.slow_at
        s1 = 1. - s
        x = self.diff
        
        if x > 0.5: real = 1. - (2 - 2*x)*s
        else:
            real = ((2*x)**3 )*s1
        
        
        #real = 1.0 - real
        real *= self.diff_distance
                
        offset = real * self.diff_dir
        offset = self.target + offset
        current = int(offset)
        offset = current - offset

        if self.songs:
            tt = self.texture_cache.textures
            for i in self.order:
                indx = current + i
                if indx >= 0 and indx < self.songs_len: 
                    _, cover, art, al, t = self.songs[indx]  
                    self.draw_cover(tt[cover], i + offset)                
                    
        
    
    def draw_cover(self, cover_texture, atx):
        """Draw a rectagle textured with cover_texture and a dimmed reflection
        at the approprieate position """
        glPushMatrix()
        

        glBindTexture(GL_TEXTURE_2D, cover_texture)
        a_atx = math.fabs(atx)
        if atx <= -1.0:
            glTranslatef( .8*atx -0.8, 0.8, -0.2*a_atx -7.0 )
            glRotatef( 80.0, 0.0, 1.0, 0.0)
        elif atx >= 1.0:
            glTranslatef( .8*atx +0.8, 0.8, -0.2*a_atx -7.0 )
            glRotatef( -80.0, 0.0, 1.0, 0.0)
        else: 
            glTranslatef( 1.6*atx, 0.8, -0.9*a_atx -6.3 )
            glRotatef( -atx*80.0, 0.0, 1.0, 0.0)
            
        glBegin( GL_QUADS )
        glColor4f(1.0,1.0,1.0,1.0)
        glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0,  0.0 )
        glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0,  0.0 )
        glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0,  0.0 )
        glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0,  0.0 )
        glEnd()

        glBegin( GL_QUADS )
        glColor4fv(self.color1)
        glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, -3.0,  0.0 )
        glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0, -3.0,  0.0 )

        glColor4fv(self.color2)
        glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0,  0.0 )
        glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0,  0.0 )              
        glEnd()
        glPopMatrix()
            			
	
def texture_from_path(path, texid):
    """Load an image file as a 2D texture using PIL"""
    from Image import open, NEAREST
    im = open(path).resize((256,256), NEAREST)
        
    try:
        # get image meta-data (dimensions) and data
        ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBA", 0, -1)
    except SystemError:
        # has no alpha channel, synthesize one, see the
        # texture module for more realistic handling
        ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBX", 0, -1)
            
    glBindTexture(GL_TEXTURE_2D, texid)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glPixelStorei(GL_UNPACK_ALIGNMENT,1)
    # copy the texture into the current texture ID
    glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
    # return the ID for use
    return texid


